package service

import (
	"bytes"
	"database/sql"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"gin-epg/internal/app/client/utils"
	"gin-epg/internal/app/common/util"
	"gin-epg/internal/app/dao"
	"gin-epg/internal/app/entity"
	"gin-epg/internal/app/model"
	"gin-epg/internal/app/store"
	"github.com/PuerkitoBio/goquery" // 导入 goquery 库
	"github.com/jinzhu/gorm"
	"io"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net/http"
	"os"
	"strings"
	"time"
)

// EpgData 代表 epg_data.json 的结构
type EpgData struct {
	Epgs []Epg `json:"epgs"`
}

// Epg 代表单个频道的数据
type Epg struct {
	Epgid string `json:"epgid"`
	Logo  string `json:"logo"`
	Name  string `json:"name"`
}

func InitChannel() error {
	db := dao.SqlSession

	// 查询所有频道
	var channels []entity.EpgChannel
	if err := db.Find(&channels).Error; err != nil {
		return fmt.Errorf("failed to query all channels: %v", err)
	}

	// 将频道名称存储在 map 中
	channelMap := make(map[string]entity.EpgChannel)
	for _, channel := range channels {
		channelMap[channel.Name] = channel
	}

	// 下载 112114 的 e.xml 这里用镜像
	// 主站  https://epg.112114.xyz/pp.xml
	// 备用1 https://diyp1000.112114.xyz/pp.xml 目前配置
	// 备用2 https://raw.githubusercontent.com/sparkssssssssss/epg/main/pp.xml
	// 备用3 https://live.fanmingming.com/e.xml 加了cf无法访问
	// 备用4 https://gitee.com/taksssss/tv/raw/main/epg/112114.xml.gz gz格式后续支持
	xmlURL := "http://diyp1000.112114.xyz/pp.xml"
	mirrorURL, err := util.GetConfigValue("mirror.mirror112114xml")
	if err != nil {
		fmt.Printf("Error: %v\n", err)
	} else {
		fmt.Printf("mirrorURL mirror112114xml: %v\n", mirrorURL)
		xmlURL = fmt.Sprintf("%s", mirrorURL)
	}

	// 使用 utils.FetchXML 获取 XML 数据
	xmlData, err := utils.FetchXML(xmlURL)
	if err != nil {
		return fmt.Errorf("failed to fetch XML: %v", err)
	}

	// 解析 XML 数据
	var channelXML model.TV
	if err := xml.Unmarshal(xmlData, &channelXML); err != nil {
		return fmt.Errorf("failed to unmarshal e.xml: %v", err)
	}

	// 遍历频道名称
	for _, xmlChannel := range channelXML.Channels {
		// 检查频道名称是否存在于 map 中
		if _, exists := channelMap[xmlChannel.DisplayName]; !exists {
			// 频道不存在，创建新的频道记录
			newChannel := entity.EpgChannel{
				Name:       xmlChannel.DisplayName,
				Source:     "112114",
				CreateTime: entity.NullTime{sql.NullTime{Time: time.Now(), Valid: true}},
			}
			if err := db.Create(&newChannel).Error; err != nil {
				fmt.Printf("Failed to create channel %s: %v\n", xmlChannel.DisplayName, err)
			} else {
				fmt.Printf("Channel %s has been successfully created\n", xmlChannel.DisplayName)
			}
		} else {
			fmt.Printf("Channel %s already exists\n", xmlChannel.DisplayName)
		}
	}

	return nil
}

// InitLogo 初始化频道 Logo
func InitLogo() error {
	db := dao.SqlSession

	// 创建 OSS 存储服务实例
	ossService := store.GlobalStoreService

	// 查询所有无logo频道
	var channels []entity.EpgChannel
	if err := db.Where("logo IS NULL OR logo = ''").Find(&channels).Error; err != nil {
		return fmt.Errorf("failed to query epg_channel table: %v", err)
	}

	log.Printf("init log for fanmingming or 112114 logo")
	// 处理每个频道的 Logo
	if err := processChannelLogo(channels, db, ossService); err != nil {
		return fmt.Errorf("error processing channels: %v", err)
	}

	// 二次查询 logo 字段为 null 或者空字符串的频道
	var channelsWithoutLogo []entity.EpgChannel
	if err := db.Where("logo IS NULL OR logo = ''").Find(&channelsWithoutLogo).Error; err != nil {
		return fmt.Errorf("failed to query channels without logo: %v", err)
	}

	log.Printf("init log for Laozhang logo")
	// 处理老张的 Logo
	if err := processLaozhangLogo(channelsWithoutLogo, db, ossService); err != nil {
		return fmt.Errorf("failed to process laozhang logo: %v", err)
	}

	// 三次查询 logo 字段为 null 或者空字符串的频道
	var channelsWithoutLogo3 []entity.EpgChannel
	if err := db.Where("logo IS NULL OR logo = ''").Find(&channelsWithoutLogo3).Error; err != nil {
		return fmt.Errorf("failed to query channels without logo: %v", err)
	}
	log.Printf("init log for ku9 logo")
	// 处理 ku9 的 Logo
	if err := processKu9Logo(channelsWithoutLogo3, db, ossService); err != nil {
		return fmt.Errorf("failed to process ku9 logo: %v", err)
	}

	return nil
}

// InitGroup 初始化频道 分组名
func InitGroup() error {
	db := dao.SqlSession

	// 查询 Group 字段为 null 或者空字符串的频道
	var channelsWithoutGroup []entity.EpgChannel
	if err := db.Where("`group` IS NULL OR `group` = ''").Find(&channelsWithoutGroup).Error; err != nil {
		return fmt.Errorf("failed to query channels without group: %v", err)
	}

	// 处理老张的 Group
	if err := processLaozhangGroup(channelsWithoutGroup, db); err != nil {
		return fmt.Errorf("failed to process laozhang group: %v", err)
	}

	return nil
}

// InitAlias 初始化频道别名
// InitAlias 初始化频道别名
func InitAlias() error {
	db := dao.SqlSession

	// 下载别名数据
	aliasURL := "http://diyp10000.112114.xyz/alias"
	resp, err := http.Get(aliasURL)
	if err != nil {
		return fmt.Errorf("failed to download alias data: %v", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("failed to download alias data: status code %d", resp.StatusCode)
	}

	// 读取响应内容
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return fmt.Errorf("failed to read alias data: %v", err)
	}

	// 使用 cheerio 解析 HTML 内容
	doc, err := goquery.NewDocumentFromReader(bytes.NewReader(body))
	if err != nil {
		return fmt.Errorf("failed to parse alias data: %v", err)
	}

	// 查找所有的 <p> 标签
	var aliasEntries []entity.EpgChannelAlias
	doc.Find("p").Each(func(i int, s *goquery.Selection) {
		text := s.Text()
		// 先按 \n 分隔，再按 --> 分隔
		lines := strings.Split(text, "\n")
		for _, line := range lines {
			parts := strings.Split(line, "-->")
			if len(parts) == 2 {
				alia := strings.TrimSpace(parts[0])
				std := strings.TrimSpace(parts[1])

				aliasEntry := entity.EpgChannelAlias{
					Name:       std,
					Alias:      alia,
					CreateTime: entity.NullTime{sql.NullTime{Time: time.Now(), Valid: true}}, // 设置创建时间为当前时间
				}
				aliasEntries = append(aliasEntries, aliasEntry)
			}
		}
	})

	// 批量插入别名数据
	for _, entry := range aliasEntries {
		var existingAlias entity.EpgChannelAlias
		if err := db.Where("name = ? AND alias = ?", entry.Name, entry.Alias).First(&existingAlias).Error; err != nil {
			if err == gorm.ErrRecordNotFound {
				if err := db.Create(&entry).Error; err != nil {
					fmt.Printf("Failed to create alias entry for %s: %v\n", entry.Name, err)
				} else {
					fmt.Printf("Alias entry for %s has been successfully created\n", entry.Name)
				}
			} else {
				fmt.Printf("Failed to check existing alias entry for %s: %v\n", entry.Name, err)
			}
		} else {
			fmt.Printf("Alias entry for %s already exists\n", entry.Name)
		}
	}

	return nil
}

// processChannelLogo 处理单个频道的 Logo
func processChannelLogo(channels []entity.EpgChannel, db *gorm.DB, ossService store.StoreService) error {
	for _, channel := range channels {
		// 下载对应的频道 logo
		// 链接1 https://live.fanmingming.com/tv/%s.png
		// 备用1 http://gitee.com/gsls200808/tv-logo/raw/master/%s.png
		// 备用2 https://diyp1000.112114.xyz/logo/%s.png
		logoURL := fmt.Sprintf("https://diyp1000.112114.xyz/logo/%s.png", channel.Name)

		resp, err := http.Get(logoURL)
		if err != nil {
			fmt.Printf("Failed to download logo for channel %s: %v\n", channel.Name, err)
			continue
		}
		defer resp.Body.Close()

		if resp.StatusCode != http.StatusOK {
			fmt.Printf("Failed to download logo for channel %s: status code %d\n", channel.Name, resp.StatusCode)
			continue
		}

		// 读取 logo 内容
		logoData, err := io.ReadAll(resp.Body)
		if err != nil {
			fmt.Printf("Failed to read logo data for channel %s: %v\n", channel.Name, err)
			continue
		}

		// 将 logo 存储到 OSS
		ossKey := fmt.Sprintf("logo/%s.png", channel.Name)
		if err := ossService.Save(ossKey, logoData); err != nil {
			fmt.Printf("Failed to save logo to OSS for channel %s: %v\n", channel.Name, err)
			continue
		}

		// 拼接 fileDownloadUrl 和 filePath
		fileDownloadUrl := "https://your-bucket-name.oss-cn-region.aliyuncs.com/"
		configFileDownloadUrl, err := util.GetConfigValue("fileDownloadUrl")
		if err == nil {
			fileDownloadUrl = configFileDownloadUrl.(string) // 进行类型断言
		}
		logoPath := fileDownloadUrl + ossKey

		// 更新 epg_channel 表的 logo 字段
		channel.Logo = logoPath
		if err := db.Model(&channel).Update("logo", logoPath).Error; err != nil {
			fmt.Printf("Failed to update logo for channel %s: %v\n", channel.Name, err)
			continue
		}

		fmt.Printf("Logo for channel %s has been successfully updated\n", channel.Name)
	}
	return nil
}

// processLaozhangLogo 处理老张的 Logo
func processLaozhangLogo(channels []entity.EpgChannel, db *gorm.DB, ossService store.StoreService) error {
	// 构造频道名和 demo URL 的 txt 文件
	txtContent := bytes.NewBufferString("")
	for _, channel := range channels {
		txtContent.WriteString(fmt.Sprintf("%s,%s\n", channel.Name, "http://192.168.1.1/a.m3u"))
	}

	if txtContent.Len() > 0 {
		// 将 txt 文件保存到本地
		tmpFile, err := ioutil.TempFile("", "channels_without_logo.txt")
		if err != nil {
			return fmt.Errorf("failed to create temporary file: %v", err)
		}
		defer os.Remove(tmpFile.Name())

		if _, err := txtContent.WriteTo(tmpFile); err != nil {
			return fmt.Errorf("failed to write to temporary file: %v", err)
		}
		if err := tmpFile.Close(); err != nil {
			return fmt.Errorf("failed to close temporary file: %v", err)
		}

		// 调用上传接口
		uploadURL := "http://epg.51zmt.top:8000/api/upload/"
		resp, err := uploadFile(uploadURL, tmpFile.Name())
		if err != nil {
			return fmt.Errorf("failed to upload file: %v", err)
		}
		defer resp.Body.Close()

		// 读取上传接口返回的内容
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return fmt.Errorf("failed to read response body: %v", err)
		}

		// 解析返回的 HTML 内容，提取 href
		href := extractHrefFromHTML(string(body))
		if href == "" {
			return fmt.Errorf("failed to extract href from response: %v", string(body))
		}

		// 下载 m3u 文件
		m3uResp, err := http.Get(href)
		if err != nil {
			return fmt.Errorf("failed to download m3u file: %v", err)
		}
		defer m3uResp.Body.Close()

		// 读取 m3u 文件内容
		m3uContent, err := ioutil.ReadAll(m3uResp.Body)
		if err != nil {
			return fmt.Errorf("failed to read m3u file content: %v", err)
		}

		// 解析 m3u 文件
		lines := strings.Split(string(m3uContent), "\n")
		for i := 0; i < len(lines); i++ {
			if strings.HasPrefix(lines[i], "#EXTINF:") {
				parts := strings.Split(lines[i], ",")
				if len(parts) >= 2 {
					tvgName := parts[1]
					tvgLogo := extractTvgLogoFromLine(lines[i])
					if tvgLogo != "" {
						// 下载 tvg-logo
						logoResp, err := http.Get(tvgLogo)
						if err != nil {
							fmt.Printf("Failed to download logo for channel %s: %v\n", tvgName, err)
							continue
						}
						defer logoResp.Body.Close()

						if logoResp.StatusCode != http.StatusOK {
							fmt.Printf("Failed to download logo for channel %s: status code %d\n", tvgName, logoResp.StatusCode)
							continue
						}

						// 读取 logo 内容
						logoData, err := io.ReadAll(logoResp.Body)
						if err != nil {
							fmt.Printf("Failed to read logo data for channel %s: %v\n", tvgName, err)
							continue
						}

						// 将 logo 存储到 OSS
						ossKey := fmt.Sprintf("logo/%s.png", tvgName)
						if err := ossService.Save(ossKey, logoData); err != nil {
							fmt.Printf("Failed to save logo to OSS for channel %s: %v\n", tvgName, err)
							continue
						}

						// 拼接 fileDownloadUrl 和 filePath
						fileDownloadUrl := "https://your-bucket-name.oss-cn-region.aliyuncs.com/"
						configFileDownloadUrl, err := util.GetConfigValue("fileDownloadUrl")
						if err == nil {
							fileDownloadUrl = configFileDownloadUrl.(string) // 进行类型断言
						}
						logoPath := fileDownloadUrl + ossKey

						// 更新 epg_channel 表的 logo 字段
						var channel entity.EpgChannel
						if err := db.Where("name = ?", tvgName).First(&channel).Error; err == nil {
							channel.Logo = logoPath
							if err := db.Model(&channel).Update("logo", logoPath).Error; err != nil {
								fmt.Printf("Failed to update logo for channel %s: %v\n", tvgName, err)
							} else {
								fmt.Printf("Logo for channel %s has been successfully updated\n", tvgName)
							}
						} else {
							fmt.Printf("Channel %s not found in database\n", tvgName)
						}
					}
				}
			}
		}
	}
	return nil
}

// processLaozhangGroup 处理老张的 Group
// processLaozhangGroup 处理老张的 Group
func processLaozhangGroup(channels []entity.EpgChannel, db *gorm.DB) error {
	// 构造频道名和 demo URL 的 txt 文件
	txtContent := bytes.NewBufferString("")
	for _, channel := range channels {
		txtContent.WriteString(fmt.Sprintf("%s,%s\n", channel.Name, "http://192.168.1.1/a.m3u"))
	}

	if txtContent.Len() > 0 {
		// 将 txt 文件保存到本地
		tmpFile, err := ioutil.TempFile("", "channels_without_group.txt")
		if err != nil {
			return fmt.Errorf("failed to create temporary file: %v", err)
		}
		defer os.Remove(tmpFile.Name())

		if _, err := txtContent.WriteTo(tmpFile); err != nil {
			return fmt.Errorf("failed to write to temporary file: %v", err)
		}
		if err := tmpFile.Close(); err != nil {
			return fmt.Errorf("failed to close temporary file: %v", err)
		}

		// 调用上传接口
		uploadURL := "http://epg.51zmt.top:8000/api/upload/"
		resp, err := uploadFile(uploadURL, tmpFile.Name())
		if err != nil {
			return fmt.Errorf("failed to upload file: %v", err)
		}
		defer resp.Body.Close()

		// 读取上传接口返回的内容
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return fmt.Errorf("failed to read response body: %v", err)
		}

		// 解析返回的 HTML 内容，提取 href
		href := extractHrefFromHTML(string(body))
		if href == "" {
			return fmt.Errorf("failed to extract href from response: %v", string(body))
		}

		// 下载 m3u 文件
		m3uResp, err := http.Get(href)
		if err != nil {
			return fmt.Errorf("failed to download m3u file: %v", err)
		}
		defer m3uResp.Body.Close()

		// 读取 m3u 文件内容
		m3uContent, err := ioutil.ReadAll(m3uResp.Body)
		if err != nil {
			return fmt.Errorf("failed to read m3u file content: %v", err)
		}

		// 解析 m3u 文件
		lines := strings.Split(string(m3uContent), "\n")
		for i := 0; i < len(lines); i++ {
			if strings.HasPrefix(lines[i], "#EXTINF:") {
				parts := strings.Split(lines[i], ",")
				if len(parts) >= 2 {
					tvgName := parts[1]
					groupTitle := extractGroupTitleFromLine(lines[i])
					if groupTitle != "" && groupTitle != "未分类" {
						// 更新 epg_channel 表的 group 字段
						var channel entity.EpgChannel
						if err := db.Where("name = ?", tvgName).First(&channel).Error; err == nil {
							channel.Group = groupTitle
							if err := db.Model(&channel).Update("group", groupTitle).Error; err != nil {
								fmt.Printf("Failed to update group for channel %s: %v\n", tvgName, err)
							} else {
								fmt.Printf("Group for channel %s has been successfully updated\n", tvgName)
							}
						} else {
							fmt.Printf("Channel %s not found in database\n", tvgName)
						}
					}
				}
			}
		}
	}
	return nil
}

// extractGroupTitleFromLine 从 m3u 行中提取 group-title
func extractGroupTitleFromLine(line string) string {
	start := strings.Index(line, "group-title=\"")
	if start == -1 {
		return ""
	}
	start += len("group-title=\"")
	end := strings.Index(line[start:], "\"")
	if end == -1 {
		return ""
	}
	return line[start : start+end]
}

// processKu9Logo 处理 ku9 的 Logo
func processKu9Logo(channels []entity.EpgChannel, db *gorm.DB, ossService store.StoreService) error {
	// 下载 epg_data.json
	// 原始url https://raw.githubusercontent.com/taksssss/tv/refs/heads/main/ku9/epg_data.json
	// 备用mock
	jsonURL := "https://raw.githubusercontent.com/taksssss/tv/refs/heads/main/ku9/epg_data.json"
	resp, err := http.Get(jsonURL)
	if err != nil {
		return fmt.Errorf("failed to download epg_data.json: %v", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("failed to download epg_data.json: status code %d", resp.StatusCode)
	}

	// 读取 JSON 数据
	jsonData, err := io.ReadAll(resp.Body)
	if err != nil {
		return fmt.Errorf("failed to read epg_data.json: %v", err)
	}

	// 解析 JSON 数据
	var epgData EpgData
	if err := json.Unmarshal(jsonData, &epgData); err != nil {
		return fmt.Errorf("failed to unmarshal epg_data.json: %v", err)
	}

	// 遍历频道数据
	for _, channel := range channels {
		for _, epg := range epgData.Epgs {
			// 检查频道名称是否匹配
			if strings.Contains(epg.Name, channel.Name) {
				// 下载对应的 logo
				logoResp, err := http.Get(epg.Logo)
				if err != nil {
					fmt.Printf("Failed to download logo for channel %s: %v\n", channel.Name, err)
					continue
				}
				defer logoResp.Body.Close()

				if logoResp.StatusCode != http.StatusOK {
					fmt.Printf("Failed to download logo for channel %s: status code %d\n", channel.Name, logoResp.StatusCode)
					continue
				}

				// 读取 logo 内容
				logoData, err := io.ReadAll(logoResp.Body)
				if err != nil {
					fmt.Printf("Failed to read logo data for channel %s: %v\n", channel.Name, err)
					continue
				}

				// 将 logo 存储到 OSS
				ossKey := fmt.Sprintf("logo/%s.png", channel.Name)
				if err := ossService.Save(ossKey, logoData); err != nil {
					fmt.Printf("Failed to save logo to OSS for channel %s: %v\n", channel.Name, err)
					continue
				}

				// 拼接 fileDownloadUrl 和 filePath
				fileDownloadUrl := "https://your-bucket-name.oss-cn-region.aliyuncs.com/"
				configFileDownloadUrl, err := util.GetConfigValue("fileDownloadUrl")
				if err == nil {
					fileDownloadUrl = configFileDownloadUrl.(string) // 进行类型断言
				}
				logoPath := fileDownloadUrl + ossKey

				// 更新 epg_channel 表的 logo 字段
				channel.Logo = logoPath
				if err := db.Model(&channel).Update("logo", logoPath).Error; err != nil {
					fmt.Printf("Failed to update logo for channel %s: %v\n", channel.Name, err)
				} else {
					fmt.Printf("Logo for channel %s has been successfully updated\n", channel.Name)
				}
			}
		}
	}
	return nil
}

// uploadFile 上传文件到指定 URL
func uploadFile(url, filePath string) (*http.Response, error) {
	file, err := os.Open(filePath)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)
	part, err := writer.CreateFormFile("myfile", filePath)
	if err != nil {
		return nil, err
	}
	_, err = io.Copy(part, file)
	if err != nil {
		return nil, err
	}
	writer.Close()

	req, err := http.NewRequest("POST", url, body)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", writer.FormDataContentType())

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

// extractHrefFromHTML 从 HTML 中提取 href
func extractHrefFromHTML(html string) string {
	start := strings.Index(html, "href=\"")
	if start == -1 {
		return ""
	}
	start += len("href=\"")
	end := strings.Index(html[start:], "\"")
	if end == -1 {
		return ""
	}
	return html[start : start+end]
}

// extractTvgLogoFromLine 从 m3u 行中提取 tvg-logo
func extractTvgLogoFromLine(line string) string {
	start := strings.Index(line, "tvg-logo=\"")
	if start == -1 {
		return ""
	}
	start += len("tvg-logo=\"")
	end := strings.Index(line[start:], "\"")
	if end == -1 {
		return ""
	}
	return line[start : start+end]
}
